/**
 *******************************************************************************
 * @file  usb/usb_dev_cdc/source/usb_bsp.c
 * @brief BSP function for USB example
 @verbatim
   Change Logs:
   Date             Author          Notes
   2020-06-12       CDT             First version
 @endverbatim
 *******************************************************************************
 * Copyright (C) 2020, Huada Semiconductor Co., Ltd. All rights reserved.
 *
 * This software component is licensed by HDSC under BSD 3-Clause license
 * (the "License"); You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                    opensource.org/licenses/BSD-3-Clause
 *
 *******************************************************************************
 */

/*******************************************************************************
 * Include files
 ******************************************************************************/
#include <stdio.h>

#include <rthw.h>
#include <rtdevice.h>

#include "usb_bsp.h"
#include "hc32_ll.h"

#include "usb_dev_user.h"
#include "usb_dev_desc.h"
#include "usb_dev_cdc_class.h"
/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/

/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/
#ifdef USB_FS_MODE      /* USBFS Core*/
#define USB_DP_PORT     (GPIO_PORT_A)
#define USB_DP_PIN      (GPIO_PIN_12)
#define USB_DM_PORT     (GPIO_PORT_A)
#define USB_DM_PIN      (GPIO_PIN_11)
#define USB_VBUS_PORT   (GPIO_PORT_A)
#define USB_VBUS_PIN    (GPIO_PIN_09)
#define USB_SOF_PORT    (GPIO_PORT_A)
#define USB_SOF_PIN     (GPIO_PIN_08)
#else                   /* USBHS Core*/
#ifdef USB_HS_EXTERNAL_PHY
/* external PHY */
#define USBH_ULPI_CLK_PORT      (GPIO_PORT_E)
#define USBH_ULPI_CLK_PIN       (GPIO_PIN_12)
#define USBH_ULPI_DIR_PORT      (GPIO_PORT_C)
#define USBH_ULPI_DIR_PIN       (GPIO_PIN_02)
#define USBH_ULPI_NXT_PORT      (GPIO_PORT_C)
#define USBH_ULPI_NXT_PIN       (GPIO_PIN_03)
#define USBH_ULPI_STP_PORT      (GPIO_PORT_C)
#define USBH_ULPI_STP_PIN       (GPIO_PIN_00)
#define USBH_ULPI_D0_PORT       (GPIO_PORT_E)
#define USBH_ULPI_D0_PIN        (GPIO_PIN_13)
#define USBH_ULPI_D1_PORT       (GPIO_PORT_E)
#define USBH_ULPI_D1_PIN        (GPIO_PIN_14)
#define USBH_ULPI_D2_PORT       (GPIO_PORT_E)
#define USBH_ULPI_D2_PIN        (GPIO_PIN_15)
#define USBH_ULPI_D3_PORT       (GPIO_PORT_B)
#define USBH_ULPI_D3_PIN        (GPIO_PIN_10)
#define USBH_ULPI_D4_PORT       (GPIO_PORT_B)
#define USBH_ULPI_D4_PIN        (GPIO_PIN_11)
#define USBH_ULPI_D5_PORT       (GPIO_PORT_B)
#define USBH_ULPI_D5_PIN        (GPIO_PIN_12)
#define USBH_ULPI_D6_PORT       (GPIO_PORT_B)
#define USBH_ULPI_D6_PIN        (GPIO_PIN_13)
#define USBH_ULPI_D7_PORT       (GPIO_PORT_E)
#define USBH_ULPI_D7_PIN        (GPIO_PIN_11)
/* 3300 reset */
#define USB_3300_RESET_PORT     (EIO_PORT1)
#define USB_3300_RESET_PIN      (EIO_USB3300_RST)
#else
/* USBHS Core, embedded PHY */
#define USB_DP_PORT     (GPIO_PORT_B)
#define USB_DP_PIN      (GPIO_PIN_15)
#define USB_DM_PORT     (GPIO_PORT_B)
#define USB_DM_PIN      (GPIO_PIN_14)
#define USB_VBUS_PORT   (GPIO_PORT_B)
#define USB_VBUS_PIN    (GPIO_PIN_13)
#define USB_SOF_PORT    (GPIO_PORT_A)
#define USB_SOF_PIN     (GPIO_PIN_04)
#endif
#endif

/*******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/

/*******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/

/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/

/*******************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/
struct rt_serial_device cdc_serial;
usb_core_instance usb_dev;

extern  void usb_isr_handler(usb_core_instance *pdev);

/**
 *******************************************************************************
 ** \brief  handle the USB interrupt
 ** \param  none
 ** \retval none
 ******************************************************************************/
static void USB_IRQ_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    usb_isr_handler(&usb_dev);

    /* leave interrupt */
    rt_interrupt_leave();
}

/**
 *******************************************************************************
 ** \brief  initialize configurations for the BSP
 ** \param  pdev: device instance
 ** \retval none
 ******************************************************************************/
void usb_bsp_init(usb_core_instance *pdev)
{
    stc_gpio_init_t stcGpioCfg;

#if (DDL_PRINT_ENABLE == DDL_ON)
    DDL_PrintfInit(BSP_PRINTF_DEVICE, BSP_PRINTF_BAUDRATE, BSP_PRINTF_PortInit);
#endif

    /* USB clock source configurate */
    CLK_SetUSBClockSrc(CLK_USBCLK_SYSCLK_DIV5);

#if (DDL_PRINT_ENABLE == DDL_ON)
    DDL_Printf("USBFS start !!\r\n");
#endif
    GPIO_StructInit(&stcGpioCfg);
   
    stcGpioCfg.u16PinAttr = PIN_ATTR_ANALOG;
    GPIO_Init(USB_DM_PORT, USB_DM_PIN, &stcGpioCfg);
    GPIO_Init(USB_DP_PORT, USB_DP_PIN, &stcGpioCfg);
    GPIO_SetFunc(USB_VBUS_PORT, USB_VBUS_PIN, GPIO_FUNC_10); //VBUS
    FCG_Fcg1PeriphClockCmd(FCG1_PERIPH_USBFS, ENABLE);
}

/**
 *******************************************************************************
 ** \brief  configure the NVIC of USB
 ** \param  none
 ** \retval none
 ******************************************************************************/
void usb_bsp_nvicconfig(void)
{
    stc_irq_signin_config_t stcIrqRegiConf;
    /* Register INT_USBFS_GLB Int to Vect.No.126 */
    stcIrqRegiConf.enIRQn = INT003_IRQn;
    /* Select interrupt function */

    stcIrqRegiConf.enIntSrc = INT_SRC_USBFS_GLB;

    /* Callback function */
    stcIrqRegiConf.pfnCallback = &USB_IRQ_Handler;
    /* Registration IRQ */
    INTC_IrqSignIn(&stcIrqRegiConf);
    /* Clear Pending */
    NVIC_ClearPendingIRQ(stcIrqRegiConf.enIRQn);
    /* Set priority */
    NVIC_SetPriority(stcIrqRegiConf.enIRQn, DDL_IRQ_PRIO_DEFAULT);
    /* Enable NVIC */
    NVIC_EnableIRQ(stcIrqRegiConf.enIRQn);
}

/**
 *******************************************************************************
 ** \brief  This function provides delay time in micro sec
 ** \param  usec : Value of delay required in micro sec
 ** \retval None
 ******************************************************************************/
void usb_udelay(const uint32_t usec)
{
    uint32_t delta = 0;
    uint32_t start, now, reload, us_tick;
    start = SysTick->VAL;
    reload = SysTick->LOAD;
    us_tick = SystemCoreClock / 1000000UL;

    do
    {
        now = SysTick->VAL;
        delta += start > now ?  start - now : reload + start - now;
    }
    while (delta < us_tick * usec);
}

/**
 *******************************************************************************
 ** \brief  This function provides delay time in milli sec
 ** \param  msec : Value of delay required in milli sec
 ** \retval None
 ******************************************************************************/
void usb_mdelay(const uint32_t msec)
{
    usb_udelay(msec * 1000UL);
}


void usb_cinit(void)
{
    usb_dev_init(&usb_dev, &user_desc, &class_cdc_cbk, &user_cb);
}

void usb_cfini(void)
{
    usb_dev_deinit(&usb_dev);

    FCG_Fcg1PeriphClockCmd(FCG1_PERIPH_USBFS, DISABLE);

    /* Disable NVIC */
    /* Register INT_USBFS_GLB Int to Vect.No.030 */
    NVIC_DisableIRQ(INT030_IRQn);

    /* sign out IRQ */
    INTC_IrqSignOut(INT030_IRQn);
}

extern void usb_cout(const uint8_t *buf, unsigned len);
extern int usb_cin(void);

/**
  * @brief       
  * @param[in]   serial  
  * @param[in]   pos  
  * @param[in]   buffer  
  * @param[in]   size  
  * @param[out]  
  * @retval      
  * @note        
  */
static rt_size_t usb_cdc_read(struct rt_device* dev, rt_off_t pos, void* buffer, rt_size_t size)
{
    (void)dev;
    (void)pos;

    uint32_t i;
    int data;
    for (i=0; i<size; i++) {
        data = usb_cin();

        if (data == -1) {
            break;
        }

        ((uint8_t *)buffer)[i] = (uint8_t)data;
    }

    return i;
}

/**
  * @brief       
  * @param[in]   serial  
  * @param[in]   pos  
  * @param[in]   buffer  
  * @param[in]   size  
  * @param[out]  
  * @retval      
  * @note        
  */
static rt_size_t usb_cdc_write(struct rt_device* dev, rt_off_t pos, const void* buffer, rt_size_t size)
{
    (void)dev;
    (void)pos;
    
    rt_size_t ret = 0;
    
    if (hc32_usb_connected) {
        usb_cout(buffer, size);
        ret = size;
    }

    return ret;
}

/**
  * @brief       控制串口配置参数
  * @param[in]   serial  
  * @param[in]   cmd  
  * @param[in]   arg  
  * @param[out]  
  * @retval      
  * @note        
  */
static rt_err_t usb_cdc_control(struct rt_device* dev, int cmd, void* arg)
{
    (void)dev;

    switch (cmd)
    {
    case RT_DEVICE_CTRL_CLR_INT:
        /* disable rx irq */
        break;
    case RT_DEVICE_CTRL_SET_INT:
        /* enable rx irq */
        break;
    case 0x20:
        (*(rt_bool_t*)arg) = (rt_bool_t)hc32_usb_connected;
        break;
    default:
        break;
    }

    return RT_EOK;
}

/**
  * @brief       将各个设备注册
  * @param[in]   serial  
  * @param[in]   name  
  * @param[in]   flag  
  * @param[out]  
  * @retval      
  * @note        
  */
int rt_hw_usb_cdc_init(void)
{
    usb_cinit();

    cdc_serial.config.baud_rate    = BAUD_RATE_115200;
    cdc_serial.config.data_bits    = DATA_BITS_8;
    cdc_serial.config.stop_bits    = STOP_BITS_1;
    cdc_serial.config.parity       = PARITY_NONE;
    cdc_serial.config.bit_order    = BIT_ORDER_LSB;
    cdc_serial.config.invert       = NRZ_NORMAL;
#if defined(RT_USING_SERIAL_V1)
    cdc_serial.config.bufsz        = APP_RX_DATA_SIZE;
#elif defined(RT_USING_SERIAL_V2)
    cdc_serial.config.rx_bufsz     = APP_RX_DATA_SIZE;
    cdc_serial.config.tx_bufsz     = APP_RX_DATA_SIZE;
#endif

    struct rt_device* device;

    device = &cdc_serial.parent;
    device->type        = RT_Device_Class_Char; //是否修改为这个？
    device->ref_count   = 0;
    device->rx_indicate = RT_NULL;
    device->tx_complete = RT_NULL;

    device->init        = RT_NULL;
    device->open        = RT_NULL;
    device->close       = RT_NULL;
    device->read        = usb_cdc_read;
    device->write       = usb_cdc_write;
    device->control     = usb_cdc_control;

    return rt_device_register(device, "vcom", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_INT_TX);
}
INIT_BOARD_EXPORT(rt_hw_usb_cdc_init);
/*******************************************************************************
 * EOF (not truncated)
 ******************************************************************************/
